package com.amadeus.personal_demo;


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.gprinter.io.PortManager;
import com.gprinter.io.UsbPort;

import java.io.IOException;
import java.util.Vector;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static android.hardware.usb.UsbManager.ACTION_USB_DEVICE_DETACHED;

public class DeviceConnFactoryManager {

    private PortManager mPort;

    private static final String TAG = DeviceConnFactoryManager.class.getSimpleName();

    private CONN_METHOD connMethod;

    private UsbDevice mUsbDevice;

    private String macAddress;

    private Context mContext;

    private int id;

    private boolean isOpenPort;

    private static DeviceConnFactoryManager[] deviceConnFactoryManagers = new DeviceConnFactoryManager[1];

    private byte[] esc = {0x10, 0x04, 0x02};
    //ESC指令查询打印机缺纸、开盖、报错三种状态
    private static final int ESC_STATE_PAPER_ERR = 0x20;
    private static final int ESC_STATE_COVER_OPEN = 0x04;
    private static final int ESC_STATE_ERR_OCCURS = 0x40;

    private byte[] tsc = {0x1b, '!', '?'};
    //TSC指令查询打印机缺纸、开盖、报错三种状态
    private static final int TSC_STATE_PAPER_ERR = 0x04;
    private static final int TSC_STATE_COVER_OPEN = 0x01;
    private static final int TSC_STATE_ERR_OCCURS = 0x80;

    private byte[] sendCommand;

    private PrintCommand currentPrintCommand;
    private static final byte FLAG = 0x10;
    private static final int READ_DATA = 10000;
    private static final String READ_DATA_CNT = "read_data_cnt";
    private static final String READ_BUFFER_ARRAY = "read_buffer_array";
    public static final String ACTION_CONN_STATE = "action_conn_state";
    public static final String ACTION_QUERY_PRINTER_STATE = "action_query_printer_state";
    public static final String STATE = "state";
    public static final String DEVICE_ID = "id";
    public static final int CONN_STATE_DISCONNECT = 0x90;
    public static final int CONN_STATE_CONNECTING = CONN_STATE_DISCONNECT << 1;
    public static final int CONN_STATE_FAILED = CONN_STATE_DISCONNECT << 2;
    public static final int CONN_STATE_CONNECTED = CONN_STATE_DISCONNECT << 3;
    private PrinterReader reader;

    public enum CONN_METHOD {

        USB("USB");

        private String name;

        private CONN_METHOD(String name) {
            this.name = name;
        }

        public String toString() {
            return this.name;
        }
    }

    public static DeviceConnFactoryManager[] getDeviceConnFactoryManagers() {
        return deviceConnFactoryManagers;
    }

    //打开端口
    public void openPort() {
        deviceConnFactoryManagers[id].isOpenPort = false;
        sendStateBroadcast(CONN_STATE_CONNECTING);
        switch (deviceConnFactoryManagers[id].connMethod) {
            case USB:
                mPort = new UsbPort(mContext, mUsbDevice);
                isOpenPort = mPort.openPort();
                if (isOpenPort) {
                    IntentFilter filter = new IntentFilter(ACTION_USB_DEVICE_DETACHED);
                    mContext.registerReceiver(usbStateReceiver, filter);
                }
                break;
            default:
                break;
        }
        //打开端口成功后检查打印机所使用的打印机指令是ESC还是TSC
        if (isOpenPort) {
            queryCommand();
        } else {
            sendStateBroadcast(CONN_STATE_FAILED);
        }
    }


    private void queryCommand() {
        //开启读取打印机返回数据线程
        reader = new PrinterReader();
        reader.start();
        //查询打印机所用指令
        queryPrinterCommand();

    }

    //获取端口连接方式
    public CONN_METHOD getConnMethod() {
        return connMethod;
    }

    //获取端口打开状态
    public boolean getConnState() {
        return isOpenPort;
    }

    //获取连接的USB设备信息
    public UsbDevice usbDevice() {
        return mUsbDevice;
    }

    //关闭端口
    public void closePort(int id) {
        if (this.mPort != null) {
            System.out.println("id ->" + id);
            this.mPort.closePort();
            isOpenPort = false;
            currentPrintCommand = null;
        }
        sendStateBroadcast(CONN_STATE_DISCONNECT);
    }

    public static void closeALLPort() {
        for (DeviceConnFactoryManager deviceConnFactoryManager : deviceConnFactoryManagers) {
            if (deviceConnFactoryManager != null) {
                Log.e(TAG, "closeAllPort() id -> " + deviceConnFactoryManager.id);
                deviceConnFactoryManager.closePort(deviceConnFactoryManager.id);
                deviceConnFactoryManagers[deviceConnFactoryManager.id] = null;
            }
        }
    }

    private DeviceConnFactoryManager(Build build) {
        this.connMethod = build.connMethod;
        this.macAddress = build.macAddress;
        this.mUsbDevice = build.usbDevice;
        this.mContext = build.context;
        this.id = build.id;
        deviceConnFactoryManagers[id] = this;
    }

    public PrintCommand getCurrentPrintCommand() {
        return deviceConnFactoryManagers[id].currentPrintCommand;
    }

    public static final class Build {
        private String macAddress;
        private UsbDevice usbDevice;
        private Context context;
        private CONN_METHOD connMethod;
        private int id;
        private int port;

        public DeviceConnFactoryManager.Build setUsbDevice(UsbDevice usbDevice) {
            this.usbDevice = usbDevice;
            return this;
        }

        public DeviceConnFactoryManager.Build setContext(Context context) {
            this.context = context;
            return this;
        }

        public DeviceConnFactoryManager.Build setPort(int port) {
            this.port = port;
            return this;
        }

        public DeviceConnFactoryManager.Build setId(int id) {
            this.id = id;
            return this;
        }

        public DeviceConnFactoryManager.Build setConnMethod(CONN_METHOD connMethod) {
            this.connMethod = connMethod;
            return this;
        }

        public DeviceConnFactoryManager build() {
            return new DeviceConnFactoryManager(this);
        }

    }

    public void sendDataImmediately(final Vector<Byte> data) {
        if (this.mPort == null) {
            return;
        }
        try {
            Log.e(TAG, "data ->" + new String(com.gprinter.utils.Utils.convertVectorByteTobytes(data), "gb2312"));
            this.mPort.writeDataImmediately(data, 0, data.size());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public int readDateImmediately(byte[] buffer) throws IOException {
        return this.mPort.readData(buffer);
    }

    //查询打印机命令
    private void queryPrinterCommand() {
        ThreadPool.getInstantiation().addTask(new Runnable() {
            @Override
            public void run() {
                sendCommand = esc;
                Vector<Byte> data = new Vector<>(esc.length);
                for (int i = 0; i < esc.length; i++) {
                    data.add(esc[i]);
                }
                sendDataImmediately(data);

                final ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder("Timer");
                final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1, threadFactoryBuilder);
                scheduledExecutorService.schedule(threadFactoryBuilder.newThread(new Runnable() {
                    @Override
                    public void run() {
                        if (currentPrintCommand == null || currentPrintCommand != PrintCommand.ESC) {
                            Log.e(TAG, Thread.currentThread().getName());
                            sendCommand = tsc;
                            Vector<Byte> data = new Vector<>(tsc.length);
                            for (int i = 0; i < tsc.length; i++) {
                                data.add(tsc[i]);
                            }
                            sendDataImmediately(data);

                            scheduledExecutorService.schedule(threadFactoryBuilder.newThread(new Runnable() {
                                @Override
                                public void run() {
                                    if (currentPrintCommand == null) {
                                        if (reader != null) {
                                            reader.cancel();
                                            mPort.closePort();
                                            isOpenPort = false;
                                            sendStateBroadcast(CONN_STATE_FAILED);
                                        }
                                    }
                                }
                            }), 2000, TimeUnit.MILLISECONDS);
                        }
                    }
                }), 2000, TimeUnit.MILLISECONDS);
            }
        });
    }

    //线程处理
    class PrinterReader extends Thread {
        private boolean isRun = false;

        private byte[] buffer = new byte[100];

        public PrinterReader() {
            isRun = true;
        }

        public void run() {
            try {
                while (isRun) {
                    int len = readDateImmediately(buffer);
                    if (len > 0) {
                        Message message = Message.obtain();
                        message.what = READ_DATA;
                        Bundle bundle = new Bundle();
                        bundle.putInt(READ_DATA_CNT, len);
                        bundle.putByteArray(READ_BUFFER_ARRAY, buffer);
                        message.setData(bundle);
                        mHandler.sendMessage(message);
                    }
                }
            } catch (Exception e) {
                if (deviceConnFactoryManagers[id] != null) {
                    closePort(id);
                }
            }
        }

        public void cancel() {
            isRun = false;
        }
    }


    //设备与打印机连接
    private Handler mHandler = new Handler() {

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case READ_DATA:
                    int cnt = msg.getData().getInt(READ_DATA_CNT);
                    byte[] buffer = msg.getData().getByteArray(READ_BUFFER_ARRAY);
                    //对查询状态的返回值做处理
                    if (buffer == null) {
                        return;
                    }
                    int result = judgeResponseType(buffer[0]);
                    //打印机连接正常
                    String status = App.getContext().getString(R.string.str_printer_conn_normal);
                    if (sendCommand == esc) {
                        //设置打印机模式为ESC模式
                        if (currentPrintCommand == null) {
                            currentPrintCommand = PrintCommand.ESC;
                            sendStateBroadcast(CONN_STATE_CONNECTED);
                        } else {
                            //查询打印机状态
                            if (result == 0) {
                                //打印机状态
                                Intent intent = new Intent(ACTION_QUERY_PRINTER_STATE);
                                intent.putExtra(DEVICE_ID, id);
                                App.getContext().sendBroadcast(intent);
                            } else if (result == 1) {
                                //查询打印机实时状态
                                if ((buffer[0] & ESC_STATE_PAPER_ERR) > 0) {
                                    //打印机没纸了
                                    status += " " + App.getContext().getString(R.string.str_printer_out_of_paper);
                                }
                                if ((buffer[0] & ESC_STATE_COVER_OPEN) > 0) {
                                    //打印机被开盖了
                                    status += " " + App.getContext().getString(R.string.str_printer_open_cover);
                                }
                                if ((buffer[0] & ESC_STATE_ERR_OCCURS) >0){
                                    //打印机出错了
                                    status += " " +App.getContext().getString(R.string.str_printer_error);
                                }
                                System.out.println(App.getContext().getString(R.string.str_state) + status);
                                Utils.toast(App.getContext(),status);
                            }
                        }
                    }else if (sendCommand == tsc){
                        //设置打印机模式为TSC模式
                        if (currentPrintCommand == null){
                            currentPrintCommand = PrintCommand.TSC;
                            sendStateBroadcast(CONN_STATE_CONNECTED);
                        }else {
                            if (cnt == 1){
                                //查询打印机实时状态
                                if ((buffer[0] & TSC_STATE_PAPER_ERR) >0){
                                    //没纸了
                                    status += " " + App.getContext().getString(R.string.str_printer_out_of_paper);
                                }
                                if ((buffer[0] & TSC_STATE_COVER_OPEN) >0){
                                    status +=" " + App.getContext().getString(R.string.str_printer_open_cover);
                                }
                                if ((buffer[0] & TSC_STATE_ERR_OCCURS) >0){
                                    status +=" " + App.getContext().getString(R.string.str_printer_error);
                                }
                                System.out.println(App.getContext().getString(R.string.str_state) + status);
                                Utils.toast(App.getContext(),status);
                            }else {
                                Intent intent = new Intent(ACTION_QUERY_PRINTER_STATE);
                                intent.putExtra(DEVICE_ID,id);
                                App.getContext().sendBroadcast(intent);
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    };


    private void sendStateBroadcast(int state) {
        Intent intent = new Intent(ACTION_CONN_STATE);
        intent.putExtra(STATE, state);
        intent.putExtra(DEVICE_ID, id);
        App.getContext().sendBroadcast(intent);
    }

    private int judgeResponseType(byte b) {
        return (byte) ((b & FLAG) >> 4);
    }

    BroadcastReceiver usbStateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            switch (action) {
                case ACTION_USB_DEVICE_DETACHED:
                    sendStateBroadcast(CONN_STATE_DISCONNECT);
                    break;
                default:
                    break;
            }
        }
    };
}
